package com.websocket.exception.service;

import com.websocket.annotation.SendTo;
import com.websocket.service.Attachment;
import com.websocket.service.WebSocketConnection;
import com.websocket.service.WebSocketTemplate;
import org.java_websocket.WebSocket;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.security.Principal;

public class ExceptionAdaptor {

    private final WebSocketTemplate template;

    public ExceptionAdaptor(WebSocketTemplate template) {
        this.template = template;
    }

    public void handle(ExceptionExecution execution, Exception exception, WebSocket conn) {
        if (execution == null) {
            exception.printStackTrace();
            return;
        }

        Object[] injectArgs = resolveArgument(execution.getMethod(), conn, exception);

        Object returnData;
        try {
            returnData = execution.invoke(injectArgs);
        } catch (InvocationTargetException e) {
            throw new com.websocket.exception.InvocationTargetException(e);
        } catch (IllegalAccessException e) {
            throw new com.websocket.exception.IllegalAccessException(e);
        }

        handleReturnData(returnData, execution.getMethod(), conn);
    }


    private void handleReturnData(Object returnData, Method method, WebSocket conn) {
        if (returnData == null) return;

        SendTo anno = method.getAnnotation(SendTo.class);
        if (anno == null) return;

        if (anno.broadcast()) {
            template.sendToAll(returnData);
        } else {
            template.sendTo(conn, returnData);
        }
    }

    private Object[] resolveArgument(Method method, WebSocket conn, Exception exception) {
        Parameter[] parameters = method.getParameters();
        Object[] injectArgs = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (WebSocketConnection.class.isAssignableFrom(parameter.getType())) {
                injectArgs[i] = new WebSocketConnection(conn);
            } else if (Principal.class.isAssignableFrom(parameter.getType())) {
                Attachment attachment = conn.getAttachment();
                injectArgs[i] = attachment.getPrincipal();
            } else if (Exception.class.isAssignableFrom(parameter.getType())) {
                injectArgs[i] = exception;
            } else {
                injectArgs[i] = null;
            }
        }
        return injectArgs;
    }
}
